﻿using Pay.Core;
using Pay.DTO;
using Pay.TO;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace Pay.Service.Cache {
    public static class CacheToDtoExtension {
        private const int cacheSeconds = 180;
        internal static string GetKey(IQueryable query, string tag) {
            string key = tag + ":" + query.GetCacheKey().ToLower();
            return key;
        }

        /// <summary>
        /// 用于LINQ时的缓存
        /// </summary>
        /// <typeparam name="IResult"></typeparam>
        /// <param name="key"></param>
        /// <param name="func"></param>
        /// <param name="cacheTag"></param>
        /// <returns></returns>
        public static IResult FromCacheLinq<IResult>(this string key, Func<IResult> func, string cacheTag) {
            key = SecureHelper.MD5(key);
            cacheTag = cacheTag + ":" + key;
            var result = CacheHelper.GetOrCreate(cacheTag, () => {
                return func();
            }, cacheSeconds);
            return result;
        }

        public static List<IResult> FromCache<TEntity, IResult>(this IQueryable<TEntity> query, Func<IQueryable<TEntity>, List<IResult>> func, string cacheTag = "")
            where IResult : class {
            if (!string.IsNullOrEmpty(cacheTag)) {
                string key = GetKey(query, cacheTag);
                var result = CacheHelper.GetOrCreate(key, () => {
                    return func(query);
                }, cacheSeconds);
                return result;
            }
            return func(query);
        }

        public static List<IResult> FromCache<TEntity, IResult>(this IQueryable<TEntity> query, string cacheTag = "")
            where IResult : class {
            if (!string.IsNullOrEmpty(cacheTag)) {
                string key = GetKey(query, cacheTag);
                var result = CacheHelper.GetOrCreate(key, () => {
                    var entity = query.ProjectTo<TEntity, IResult>();
                    return entity;
                }, cacheSeconds);
                return result;
            }
            return query.ProjectTo<TEntity, IResult>();
        }

        public static IResult FromCacheFirstOrDefault<TEntity, IResult>(this IQueryable<TEntity> query, Func<IQueryable<TEntity>, IResult> func, string cacheTag = "")
            where IResult : class {
            if (!string.IsNullOrEmpty(cacheTag)) {
                string key = GetKey(query, cacheTag);
                var result = CacheHelper.GetOrCreate(key, () => {
                    return func(query);
                }, cacheSeconds);
                return result;
            }
            return func(query);
        }

        public static IResult FromCacheFirstOrDefault<TEntity, IResult>(this IQueryable<TEntity> query, string cacheTag = "")
            where IResult : class {
            if (!string.IsNullOrEmpty(cacheTag)) {
                string key = GetKey(query, cacheTag);
                var result = CacheHelper.GetOrCreate(key, () => {
                    var entity = query.FirstOrDefault().ToDtoObject<IResult>();
                    return entity;
                }, cacheSeconds);
                return result;
            }
            return query.FirstOrDefault().ToDtoObject<IResult>();
        }

        public static PagedResult<IResult> FromCachePaged<TEntity, IResult>(this IQueryable<TEntity> query,
            int pageNumber, int pageSize, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, Func<IQueryable<TEntity>, IQueryable<TEntity>, PagedResult<IResult>> func, string cacheTag = "")
            where TEntity : class {
            IQueryable<TEntity> count_query = query;
            query = orderBy(query).Skip((pageNumber - 1) * pageSize).Take(pageSize);

            if (!string.IsNullOrEmpty(cacheTag)) {
                string key = GetKey(query, cacheTag);
                var result = CacheHelper.GetOrCreate(key, () => {
                    return func(query, count_query);
                }, cacheSeconds);
                return result;
            }
            return func(query, count_query);
        }

        /// <summary>
        /// 分页方法
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="IResult"></typeparam>
        /// <param name="dbSet"></param>
        /// <param name="pageNumber"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderBy"></param>
        /// <param name="cacheTag"></param>
        /// <returns></returns>
        public static PagedResult<IResult> FromCachePaged<TEntity, IResult>(this DbSet<TEntity> dbSet,
            int pageNumber, int pageSize, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, string cacheTag = "") where TEntity : class {
            return dbSet.FromCachePaged<TEntity, IResult>(pageNumber, pageSize, null, orderBy, cacheTag);
        }

        /// <summary>
        /// 分页方法
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="IResult"></typeparam>
        /// <param name="dbSet"></param>
        /// <param name="filter"></param>
        /// <param name="pageNumber"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderBy"></param>
        /// <param name="cacheTag"></param>
        /// <returns></returns>
        public static PagedResult<IResult> FromCachePaged<TEntity, IResult>(this DbSet<TEntity> dbSet,
            int pageNumber, int pageSize, Expression<Func<TEntity, bool>> filter, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, string cacheTag = "") where TEntity : class {
            if (filter == null)
                filter = c => true;
            IQueryable<TEntity> count_query = dbSet.Where(filter);
            IQueryable<TEntity> query = orderBy(count_query).Skip((pageNumber - 1) * pageSize).Take(pageSize);

            if (!string.IsNullOrEmpty(cacheTag)) {
                string key = GetKey(query, cacheTag);
                var result = CacheHelper.GetOrCreate(key,
                    () => {
                        var list = query.AsNoTracking().ProjectTo<TEntity, IResult>();
                        return new PagedResult<IResult>(list, count_query.Count(), pageNumber, pageSize);
                    }, cacheSeconds);
                return result;
            } else {
                var list = query.AsNoTracking().ProjectTo<TEntity, IResult>();
                return new PagedResult<IResult>(list, count_query.Count(), pageNumber, pageSize);
            }
        }
    }
}
